package com.wangtianxiang.sxp.dbbase.config.processor;

import com.baomidou.mybatisplus.core.config.GlobalConfig;
import com.wangtianxiang.sxp.dbbase.config.db.SxpDbConfigProperties;
import com.wangtianxiang.sxp.dbbase.config.resolve.PropertiesResolve;
import org.apache.ibatis.plugin.Interceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.util.Assert;

import javax.sql.DataSource;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class SxpDbBaseBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor,EnvironmentAware , InitializingBean {

	private static final Logger logger = LoggerFactory.getLogger(SxpDbBaseBeanDefinitionRegistryPostProcessor.class);
	private final Map<String, GlobalConfig> globalConfigMap;
	private final Map<String, List<Interceptor>> interceptorsMap;

	private SxpDbConfigProperties sxpDbConfigProperties;

	private ConfigurableEnvironment  configurableEnvironment;

	private	String configPrefix;

	private SxpDataBeanRegister beanRegister;

	private BeanDefinitionRegistry registry;

	Map<String, AnnotatedBeanDefinition> customBeansBeanDefinition = new HashMap<>();


	private SxpInstantiationAwareBeanPostProcessorAdapter postProcessorAdapter;

	public SxpDbBaseBeanDefinitionRegistryPostProcessor(String configPrefix){
		this.configPrefix=configPrefix;
		this.postProcessorAdapter =new SxpInstantiationAwareBeanPostProcessorAdapter();
		this.postProcessorAdapter.setCustomBeansBeanDefinition(customBeansBeanDefinition);
		this.globalConfigMap = new HashMap<>();
		this.interceptorsMap = new HashMap<>();
	}

	// bean 的名称生成器.
	private BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();

	public SxpDbBaseBeanDefinitionRegistryPostProcessor(String configPrefix,
                                                        Map<String, GlobalConfig> globalConfigMap,
                                                        Map<String, List<Interceptor>> interceptorsMap) {
		this.configPrefix=configPrefix;
		this.postProcessorAdapter =new SxpInstantiationAwareBeanPostProcessorAdapter();
		this.postProcessorAdapter.setCustomBeansBeanDefinition(customBeansBeanDefinition);
		this.globalConfigMap = globalConfigMap != null ? globalConfigMap : new HashMap<>();
		this.interceptorsMap = interceptorsMap != null ? interceptorsMap : new HashMap<>();
	}

	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		logger.info("the bean name of datasources are:{}",Arrays.toString(beanRegister.getDatasourceMap().keySet().toArray()));
		beanFactory.addBeanPostProcessor(postProcessorAdapter);
		try {
			final Map<String, DataSource>  dataSourceBeans= beanFactory.getBeansOfType(DataSource.class);
			beanRegister.registerMybatisConfig(registry, beanNameGenerator,dataSourceBeans, globalConfigMap, interceptorsMap);
		} catch (Exception e) {
			logger.error("registerMybatisConfig failed, please check you config(sunch as *.properties or *.xml),error:{}", e.getMessage());
		}
	}

	@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
		if(null==this.registry){
			this.registry= registry;
			postProcessorAdapter.setRegistry(registry);
		}
		Assert.notNull(sxpDbConfigProperties, "sxpDbConfigProperties can not be null");
		Assert.notEmpty(sxpDbConfigProperties.getDatasources(),"sxp datasource can't be empty");
		beanRegister.registerDatasource(registry, beanNameGenerator);
	}

	/**
	 * 注册bean
	 * @param registry
	 * @param name
	 * @param annotatedBeanDefinition
	 */
	@SuppressWarnings("unused")
	private void registerBean(BeanDefinitionRegistry registry, String name, AnnotatedBeanDefinition annotatedBeanDefinition) {

		// 可以自动生成name
		String beanName = (null != name ? name
				: this.beanNameGenerator.generateBeanName(annotatedBeanDefinition, registry));
		// bean注册的holer类.
		BeanDefinitionHolder beanDefinitionHolder = new BeanDefinitionHolder(annotatedBeanDefinition, beanName);
		// 使用bean注册工具类进行注册.
		BeanDefinitionReaderUtils.registerBeanDefinition(beanDefinitionHolder, registry);
	}

	@Override
	public void setEnvironment(Environment environment) {
		configurableEnvironment = (ConfigurableEnvironment)environment;
		final MutablePropertySources propertyResolver = configurableEnvironment.getPropertySources();
		sxpDbConfigProperties = PropertiesResolve.resolve(propertyResolver,configPrefix,SxpDbConfigProperties.class);
		sxpDbConfigProperties.getDatasources().forEach((key,value)->{
			try {
				value.afterPropertiesSet();
			} catch (Exception e) {
				logger.error("datasource[{}] config error :{}",key,e.getMessage());
				throw new RuntimeException(e);
			}
		});
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		Assert.hasText(configPrefix, "'configPrefix' must not be empty");
		Assert.notNull(sxpDbConfigProperties, "'sxpDbConfigProperties' is't configgured!");
		beanRegister = new SxpDataBeanRegister(sxpDbConfigProperties,customBeansBeanDefinition);
	}


}
